/*
* Copyright 2013 Thomas Bollmeier <tbollmeier@web.de>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

var ooptimus = {};

(function (exports) {

	"use strict";

	/** Creates a new Class 
	 * -> SuperClass (optional): Constructor of super class
	 * -> initMethod: Initialization method (to be called by constructor)
	 * <- Constructor for new Class
	 */
	function defClass(superClass, initMethod) {

		var 
			superCls = null,
			init = null,
			clone = function(obj) {
			
				var Helper = function(){};
				Helper.prototype = obj;
			
				return new Helper();
			
			},
			constructor = function() {

				init.apply(this, arguments);
	
			};

		if (typeof initMethod !== "undefined") {
			superCls = superClass;
			init = initMethod;
		} else if (typeof superClass !== "undefined") {
			init = superClass;
		} else {
			throw "Number of arguments must be 1 or 2!";
		}

		if(superCls) {
			constructor.prototype = clone(superCls.prototype);
			constructor.prototype.constructor = constructor;
		}
		
		constructor.prototype.superClass = superCls;

		function superMeth(ego, name, args) {
			
			var 
				current = superMeth.stack[superMeth.stack.length - 1], // <-- current level
				cls = current.prototype.superClass,
				res = null;
			
			// Find implementation:
			while (!cls.prototype.hasOwnProperty(name)) {
				cls = cls.prototype.superClass;
				if (!cls) {
					throw new Error("Method '" + name + "' not found");
				}
			}
			
			// Adjust level info:
			superMeth.stack.push(cls);
			
			// Call implementation:
			res = cls.prototype[name].apply(ego, args);
			
			// Return to previous level:
			superMeth.stack.pop();
			
			return res;
			
		}
		
		superMeth.stack = [constructor];
		
		constructor.prototype.superMethod = function(name) {
			
			var argsArray = Array.prototype.slice.call(arguments);
			
			return superMeth(this, name, argsArray.slice(1));

		};

		constructor.prototype.superInit = function () {

			superMeth(this, 'constructor', arguments);

		};
		
		constructor.prototype.proxy = function(method) {
			var 
				that = this,
				meth = typeof method === "string" ? that[method] : method;
			return function() {
				return meth.apply(that, arguments);	
			};
		};
		
		constructor.def = function(name, value) {
			constructor.prototype[name] = value;
			return constructor;	
		};

		constructor.defStatic = function(name, value) {
			constructor[name] = value;
			return constructor;	
		};
		
		constructor.realize = function (intf) {

			var memberName;
			
			for (memberName in intf) {
				if (intf.hasOwnProperty(memberName)) {
					constructor.prototype[memberName] = intf[memberName];					
				}
			}
			
			return constructor;
		};
		
		return constructor;

	}

	exports.defClass = defClass;
	
}(ooptimus));
